#include "EffectManager.h"
#include "Game.h"



const DWORD VERTEX::FVF = D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX6;

EffectManager::EffectManager()
{
	    
}
void EffectManager::Init()
{
	m_bBasicRender=true;
	m_bWireframe=true;
	DXUTInit( true, true );
	
	FLOAT BlurConst=4.0f;
	m_vFocalPlane = D3DXVECTOR4( 0.0f, 0.0f, 1.0f, -2.5f );

	TheD3DApp::Instance()->GetD3DDev()->GetViewport( &m_ViewportFB );
	
	// Backbuffer viewport is identical to frontbuffer, except starting at 0, 0
	m_ViewportOffscreen = m_ViewportFB;
	m_ViewportOffscreen.X = 0;
	m_ViewportOffscreen.Y = 0;

	m_pBackBuffer=NULL;
	m_pRenderToSurface =NULL;
	//pBackBufferSurfaceDesc=DXUTGetD3D9BackBufferSurfaceDesc();

	TheD3DApp::Instance()->GetD3DDev()->GetRenderTarget(0,&m_pBackBuffer);
	m_pBackBuffer->GetDesc(&m_pBackBufferSurfaceDesc);

	HRESULT hr;
	// Create fullscreen renders target texture
	hr = D3DXCreateTexture( TheD3DApp::Instance()->GetD3DDev(), m_pBackBufferSurfaceDesc.Width, m_pBackBufferSurfaceDesc.Height,
		1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_pBlurTex );
	if( FAILED( hr ) )
	{
		// Fallback to a non-RT texture
		V( D3DXCreateTexture( TheD3DApp::Instance()->GetD3DDev(), m_pBackBufferSurfaceDesc.Width, m_pBackBufferSurfaceDesc.Height,
			1, 0, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_pBlurTex ) );
	}

	m_pBlurTex->GetSurfaceLevel(0,&m_pBlurSurf);

	//D3DSURFACE_DESC desc;

	//D3DXCreateRenderToSurface(TheD3DApp::Instance()->GetD3DDev(),desc.Width,desc.Height,desc.Format,true,D3DFMT_D16,&pRenderToSurface);

	D3DSURFACE_DESC desc;
	m_pBlurSurf->GetDesc( &desc );

	FLOAT fWidth5 = ( FLOAT )m_pBackBufferSurfaceDesc.Width - 0.5f;
	FLOAT fHeight5 = ( FLOAT )m_pBackBufferSurfaceDesc.Height - 0.5f;

	FLOAT fHalf = BlurConst;
	FLOAT fOffOne = fHalf * 0.5f;
	FLOAT fOffTwo = fOffOne * sqrtf( 3.0f );

	FLOAT fTexWidth1 = ( FLOAT )m_pBackBufferSurfaceDesc.Width / ( FLOAT )desc.Width;
	FLOAT fTexHeight1 = ( FLOAT )m_pBackBufferSurfaceDesc.Height / ( FLOAT )desc.Height;

	FLOAT fWidthMod = 1.0f / ( FLOAT )desc.Width;
	FLOAT fHeightMod = 1.0f / ( FLOAT )desc.Height;

	m_Vertex[0].pos = D3DXVECTOR4( fWidth5, 0.5f, 0.0f, 1.0f );
	m_Vertex[0].clr = D3DXCOLOR( 0.5f, 0.5f, 0.5f, 0.66666f );
	m_Vertex[0].tex1 = D3DXVECTOR2( fTexWidth1, 0.0f );
	m_Vertex[0].tex2 = D3DXVECTOR2( fTexWidth1, 0.0f - fHalf * fHeightMod );
	m_Vertex[0].tex3 = D3DXVECTOR2( fTexWidth1 - fOffTwo * fWidthMod, 0.0f - fOffOne * fHeightMod );
	m_Vertex[0].tex4 = D3DXVECTOR2( fTexWidth1 + fOffTwo * fWidthMod, 0.0f - fOffOne * fHeightMod );
	m_Vertex[0].tex5 = D3DXVECTOR2( fTexWidth1 - fOffTwo * fWidthMod, 0.0f + fOffOne * fHeightMod );
	m_Vertex[0].tex6 = D3DXVECTOR2( fTexWidth1 + fOffTwo * fWidthMod, 0.0f + fOffOne * fHeightMod );

	m_Vertex[1].pos = D3DXVECTOR4( fWidth5, fHeight5, 0.0f, 1.0f );
	m_Vertex[1].clr = D3DXCOLOR( 0.5f, 0.5f, 0.5f, 0.66666f );
	m_Vertex[1].tex1 = D3DXVECTOR2( fTexWidth1, fTexHeight1 );
	m_Vertex[1].tex2 = D3DXVECTOR2( fTexWidth1, fTexHeight1 - fHalf * fHeightMod );
	m_Vertex[1].tex3 = D3DXVECTOR2( fTexWidth1 - fOffTwo * fWidthMod, fTexHeight1 - fOffOne * fHeightMod );
	m_Vertex[1].tex4 = D3DXVECTOR2( fTexWidth1 + fOffTwo * fWidthMod, fTexHeight1 - fOffOne * fHeightMod );
	m_Vertex[1].tex5 = D3DXVECTOR2( fTexWidth1 - fOffTwo * fWidthMod, fTexHeight1 + fOffOne * fHeightMod );
	m_Vertex[1].tex6 = D3DXVECTOR2( fTexWidth1 + fOffTwo * fWidthMod, fTexHeight1 + fOffOne * fHeightMod );

	m_Vertex[2].pos = D3DXVECTOR4( -0.5f, -0.5f, 0.0f, 1.0f );
	m_Vertex[2].clr = D3DXCOLOR( 0.5f, 0.5f, 0.5f, 0.66666f );
	m_Vertex[2].tex1 = D3DXVECTOR2( 0.0f, 0.0f );
	m_Vertex[2].tex2 = D3DXVECTOR2( 0.0f, 0.0f - fHalf * fHeightMod );
	m_Vertex[2].tex3 = D3DXVECTOR2( 0.0f - fOffTwo * fWidthMod, 0.0f - fOffOne * fHeightMod );
	m_Vertex[2].tex4 = D3DXVECTOR2( 0.0f + fOffTwo * fWidthMod, 0.0f - fOffOne * fHeightMod );
	m_Vertex[2].tex5 = D3DXVECTOR2( 0.0f - fOffTwo * fWidthMod, 0.0f + fOffOne * fHeightMod );
	m_Vertex[2].tex6 = D3DXVECTOR2( 0.0f + fOffTwo * fWidthMod, 0.0f + fOffOne * fHeightMod );

	m_Vertex[3].pos = D3DXVECTOR4( -0.5f, fHeight5, 0.0f, 1.0f );
	m_Vertex[3].clr = D3DXCOLOR( 0.5f, 0.5f, 0.5f, 0.66666f );
	m_Vertex[3].tex1 = D3DXVECTOR2( 0.0f, fTexHeight1 );
	m_Vertex[3].tex2 = D3DXVECTOR2( 0.0f, fTexHeight1 - fHalf * fHeightMod );
	m_Vertex[3].tex3 = D3DXVECTOR2( 0.0f - fOffTwo * fWidthMod, fTexHeight1 - fOffOne * fHeightMod );
	m_Vertex[3].tex4 = D3DXVECTOR2( 0.0f + fOffTwo * fWidthMod, fTexHeight1 - fOffOne * fHeightMod );
	m_Vertex[3].tex5 = D3DXVECTOR2( 0.0f + fOffTwo * fWidthMod, fTexHeight1 + fOffOne * fHeightMod );
	m_Vertex[3].tex6 = D3DXVECTOR2( 0.0f - fOffTwo * fWidthMod, fTexHeight1 + fOffOne * fHeightMod );
}
void EffectManager::AddEffect(std::string name, Effect* e)
{
	m_EffectMap[name] = e;
}
Effect* EffectManager::Get(std::string name)
{
	Effect* res = m_EffectMap[name];
	if (!res)
	{
		MessageBox( NULL, L"Effect not Loaded", L"Error", MB_OK );
	}
	return res;
}
void EffectManager::BasicRender(Camera* camera, std::vector<GameObject*> GO)
{
	//TheD3DApp::Instance()->GetD3DDev()->SetVertexDeclaration(TheGame::Instance()->m_VertexDec);

	D3DXMATRIX mWorld;
	D3DXMATRIX mView;
	D3DXMATRIX mProjection;
	D3DXMATRIX mTranslate;
	D3DXMATRIX mScaling;
	D3DXMATRIX mRotationX;
	D3DXMATRIX mRotationY;
	D3DXMATRIX mRotationZ;	
	//HRESULT hr;
	TheD3DApp::Instance()->GetD3DDev()->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 100), 1.0f, 0);

	
	if(m_bWireframe)
	{
		TheD3DApp::Instance()->GetD3DDev()->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
	}
	else
	{
		TheD3DApp::Instance()->GetD3DDev()->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
	}

	TheD3DApp::Instance()->GetD3DDev()->BeginScene();
	for(unsigned int i=0;i<GO.size();++i)
	{
		if(!GO[i])
		{
			MessageBox(NULL,L"Game Object not available",L"EffectManager Error",MB_OK);
		}
		mView = camera->GetViewMatrix();
		mProjection = camera->GetProjectionMatrix();
		D3DXMatrixTranslation(&mWorld, 0, 0, 0);

		D3DXMatrixIdentity(&mTranslate);
		D3DXMatrixIdentity(&mScaling);
		D3DXMatrixIdentity(&mRotationX);
		D3DXMatrixIdentity(&mRotationY);
		D3DXMatrixIdentity(&mRotationZ);

		D3DXMatrixRotationX(&mRotationX,GO[i]->GetRotX());

		D3DXMatrixRotationY(&mRotationY,GO[i]->GetRotY());
		D3DXMatrixRotationZ(&mRotationZ,GO[i]->GetRotZ());

		D3DXMatrixScaling(&mScaling, GO[i]->GetScaleX(), GO[i]->GetScaleY(), GO[i]->GetScaleZ());

		D3DXMatrixTranslation(&mTranslate, GO[i]->GetPosX(), GO[i]->GetPosY(), GO[i]->GetPosZ());

		mWorld = mScaling * (mRotationY * mRotationX * mRotationZ) * mTranslate;

		// Get Inverse Transpose of World Matrix
		D3DXMATRIX m;
		D3DXMatrixInverse(&m, NULL, &mWorld);
		D3DXMATRIX j;
		D3DXMatrixTranspose(&j, &m );
		D3DXMATRIX WorldInverseTranspose = j;

		D3DXMATRIX WVP = mWorld * mView * mProjection;

		D3DXMATRIX EyePoint = camera->GetEyePoint();
		TheD3DApp::Instance()->GetD3DDev()->SetTransform(D3DTS_WORLD,&mWorld);
		TheD3DApp::Instance()->GetD3DDev()->SetTransform(D3DTS_VIEW,&mView);
		TheD3DApp::Instance()->GetD3DDev()->SetTransform(D3DTS_PROJECTION,&mProjection);
		GO[i]->Render();
	}
	TheD3DApp::Instance()->GetD3DDev()->EndScene();  
}
void EffectManager::Render(std::string name, Camera* camera, std::vector<GameObject*> GO)
{
	if (m_bBasicRender)
	{
		BasicRender(camera, GO);
		return;
	}
	TheD3DApp::Instance()->GetD3DDev()->SetVertexDeclaration(TheGame::Instance()->m_VertexDec);
	Effect* effect = Get(name);
	D3DXMATRIX mWorld;
	D3DXMATRIX mView;
	D3DXMATRIX mProjection;
	D3DXMATRIX mTranslate;
	D3DXMATRIX mScaling;
	D3DXMATRIX mRotationX;
	D3DXMATRIX mRotationY;
	D3DXMATRIX mRotationZ;	
	HRESULT hr;

	TheD3DApp::Instance()->GetD3DDev()->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 100), 1.0f, 0);
	
	TheD3DApp::Instance()->GetD3DDev()->BeginScene();
	for(unsigned int i=0;i<GO.size();++i)
	{	

		mView = camera->GetViewMatrix();
		mProjection = camera->GetProjectionMatrix();
		D3DXMatrixTranslation(&mWorld, 0, 0, 0);

		D3DXMatrixIdentity(&mTranslate);
		D3DXMatrixIdentity(&mScaling);
		D3DXMatrixIdentity(&mRotationX);
		D3DXMatrixIdentity(&mRotationY);
		D3DXMatrixIdentity(&mRotationZ);

		if(!GO[i])
		{
			MessageBox(NULL,L"Game Object not available",L"EffectManager",MB_OK);
		}


		D3DXMatrixRotationX(&mRotationX,GO[i]->GetRotX());

		D3DXMatrixRotationY(&mRotationY,GO[i]->GetRotY());
		D3DXMatrixRotationZ(&mRotationZ,GO[i]->GetRotZ());

		D3DXMatrixScaling(&mScaling, GO[i]->GetScaleX(), GO[i]->GetScaleY(), GO[i]->GetScaleZ());

		D3DXMatrixTranslation(&mTranslate, GO[i]->GetPosX(), GO[i]->GetPosY(), GO[i]->GetPosZ());

		mWorld = mScaling * (mRotationY * mRotationX * mRotationZ) * mTranslate;

		// Get Inverse Transpose of World Matrix
		D3DXMATRIX m;
		D3DXMatrixInverse(&m, NULL, &mWorld);
		D3DXMATRIX j;
		D3DXMatrixTranspose(&j, &m );
		D3DXMATRIX inv = j;

		effect->GetEffect()->SetMatrix("g_mWorld", &mWorld);
		D3DXMATRIX wvp = mWorld*mView*mProjection;
		effect->GetEffect()->SetMatrix("g_mWorldViewProjection", &wvp);
		

		D3DXMATRIX EyePoint = camera->GetEyePoint();
		effect->GetEffect()->SetValue("g_MaterialAmbientColour",&D3DXVECTOR4(0.3,0.3,0.3,1),sizeof(D3DXVECTOR4));
		effect->GetEffect()->SetValue("g_MaterialDiffuseColour",&D3DXVECTOR4(0.4,0.4,0.4,1),sizeof(D3DXVECTOR4));

		effect->GetEffect()->SetValue("g_LightDir",&D3DXVECTOR3(10,-10,-2),sizeof(D3DXVECTOR3));
		effect->GetEffect()->SetValue("g_LightDiffuse",&D3DXVECTOR4(0.2,0.2,0.2,1),sizeof(D3DXVECTOR4));
		effect->GetEffect()->SetValue("g_LightAmbient",&D3DXVECTOR4(0.1,0.1,0.1,1),sizeof(D3DXVECTOR4));

		effect->GetEffect()->SetValue("ViewVector", &EyePoint, sizeof(EyePoint));

		UINT iPass, cPasses;
		HRESULT hr = effect->GetEffect()->Begin(&cPasses,0);

		for(iPass=0;iPass<cPasses;iPass++)
		{
			effect->GetEffect()->BeginPass(iPass);
			GO[i]->Render();
			effect->GetEffect()->EndPass();
			
		}
		effect->GetEffect()->End();
		
	}

	TheD3DApp::Instance()->GetD3DDev()->EndScene(); 

}
void EffectManager::Clean()
{
	m_pBlurTex->Release();
	m_pBlurSurf->Release();
	m_pBackBuffer->Release();
	if(m_pRenderToSurface)
	{
		m_pRenderToSurface->Release();
	}
}